#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#===============================================================================
# Copyright 2011 zod.yslin
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# 
# Author: zod.yslin
# Email: 
# File Name: syntax_changed.py
# Description: 
#   1. PEP 3109 -- Raising Exceptions in Python 3000
#   2. PEP 3134 -- Exception Chaining and Embedded Tracebacks
#   3. PEP 3110 -- Catching Exceptions in Python 3000
#   4. PEP 3115 -- Metaclasses in Python 3000
#   5. List comprehensions no longer support the syntactic form [... for var in item1, item2, ...].
# Edit History: 
#   2011-10-24    File created.
#===============================================================================

#===============================================================================
#   1. PEP 3109 -- Raising Exceptions in Python 3000
#                  One of Python's guiding maxims is "there should be one 
#                  -- and preferably only one -- . 
#                  Python 2.x's raise statement violates this principle, 
#                  permitting multiple ways of expressing the same thought.
#      Example: these are the same in 2.x
#           raise ValueError, 'blah'
#           raise ValueError("blah")
#===============================================================================
# Grammar Changes
# 1. Zero- and one-expression raise statements will be left intact.

# 2. Two-expression raise statements will be converted from
#   raise_stmt: raise E, V
# to
#   raise_stmt: raise E(V)

try:
    raise NameError('hello')
except NameError as msg:
    print(msg)

# Changes to Builtin Types
# Two-expression throw() calls will be converted from
#   generator.throw(E, V)
# to 
#   generator.throw(E(V))

# Semantic Changes
# 3. Three-expression raise statements will be converted from
#   raise E, V, T
# to 
#   e = E(V)
#   e.__traceback__ = T
#   raise e

# Three-expression throw() calls will be converted from
#   generator.throw(E, V, T)
# to
#   e = E(V)
#   e.__traceback__ = T
#   generator.throw(e)
# See point #5 for a caveat to this transformation.

# 4. Two- and three-expression raise statements where E is a tuple literal can be converted 
#    automatically using 2to3's raise fixer. raise statements where E is a non-literal tuple, 
#    e.g., the result of a function call, will need to be converted manually.

# 5. Two- and three-expression raise statements where E is an exception class and V is an exception instance 
#    will need special attention. These cases break down into two camps:
#    1. raise E, V as a long-hand version of the zero-argument raise statement. As an example, assuming F is a subclass of E    
#        try:
#            something()
#        except F as V:
#            raise F(V)
#        except E as V:
#            handle(V)
#    This would be better expressed as
#        try:
#            something()
#        except F:
#            raise
#        except E as V:
#            handle(V)
#    2. raise E, V as a way of "casting" an exception to another class. Taking an example from distutils.compiler.unixcompiler
#        try:
#            self.spawn(pp_args)
#        except DistutilsExecError as msg:
#            raise CompileError(msg)
#    This would be better expressed as
#        try:
#            self.spawn(pp_args)
#        except DistutilsExecError as msg:
#            raise CompileError from msg
#    Using the raise ... from ... syntax introduced in PEP 344.


#===============================================================================
#   2. PEP 3134 -- Exception Chaining and Embedded Tracebacks
#                  
#===============================================================================


#===============================================================================
#   3. PEP 3110 -- Catching Exceptions in Python 3000
#                  Eliminate ambiguities in Python's grammar, simplify exception 
#                  classes, simplify garbage collection for exceptions and reduce 
#                  the size of the language in Python 3.0.
#===============================================================================

#===============================================================================
#   4. PEP 3115 -- Metaclasses in Python 3000
#                  changing the syntax for declaring metaclasses,
#                  and alters the semantics for how classes with metaclasses are
#                  constructed.
#                  The module-global __metaclass__ variable is no longer supported.
#===============================================================================
"""
Here's a simple example of a metaclass which creates a list of
the names of all class members, in the order that they were
declared:
"""
# The custom dictionary
class member_table(dict):
    def __init__(self):
        self.member_names = []

    def __setitem__(self, key, value):
        # if the key is not already defined, add to the
        # list of keys.
        if key not in self:
            self.member_names.append(key)

        # Call superclass
        dict.__setitem__(self, key, value)

# The metaclass
class OrderedClass(type):

    # The prepare function
    @classmethod
    def __prepare__(metacls, name, bases): # No keywords in this case
        """
        This attribute is named __prepare__, which is invoked as a function
        before the evaluation of the class body. The __prepare__ function
        takes two positional arguments, and an arbitrary number of keyword
        arguments. The two positional arguments are:
          'name' - the name of the class being created.
          'bases' - the list of base classes.
        """
        return member_table()

    # The metaclass invocation
    def __new__(cls, name, bases, classdict):
        # Note that we replace the classdict with a regular
        # dict before passing it to the superclass, so that we
        # don't continue to record member names after the class
        # has been created.
        result = type.__new__(cls, name, bases, dict(classdict))
        result.member_names = classdict.member_names
        return result

class MyClass(metaclass=OrderedClass):
    # method2 goes in array element 1
    def method2(self):
        pass
    # method1 goes in array element 2
    def method1(self):
        pass


print(MyClass.__dict__)
print(MyClass.member_names)


#===============================================================================
#   5. List comprehensions no longer support the syntactic form [... for var in item1, item2, ...].
#      Use [... for var in (item1, item2, ...)] instead. 
#===============================================================================
print([i for i in (1,2,3)])
